/*
Copyright 2024 MySQL Parser Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package sqlparser

import (
	"fmt"
	"strings"
)

// Statement implementations

// Select represents a SELECT statement
type Select struct {
	Cache       *bool
	Distinct    bool
	StraightJoin bool
	SQLCalcFoundRows bool
	SelectExprs SelectExprs
	From        TableExprs
	Where       *Where
	GroupBy     GroupBy
	Having      *Where
	OrderBy     OrderBy
	Limit       *Limit
	Lock        Lock
}

// iStatement implements Statement
func (node *Select) iStatement() {}

// Format formats the node
func (node *Select) Format(buf *TrackedBuffer) {
	buf.WriteString("select")
	if node.Distinct {
		buf.WriteString(" distinct")
	}
	if len(node.SelectExprs) > 0 {
		buf.WriteString(" ")
		node.SelectExprs.Format(buf)
	}
	if len(node.From) > 0 {
		buf.WriteString(" from ")
		node.From.Format(buf)
	}
	if node.Where != nil {
		buf.WriteString(" ")
		node.Where.Format(buf)
	}
	if len(node.GroupBy) > 0 {
		buf.WriteString(" group by ")
		node.GroupBy.Format(buf)
	}
	if node.Having != nil {
		buf.WriteString(" ")
		node.Having.Format(buf)
	}
	if len(node.OrderBy) > 0 {
		buf.WriteString(" order by ")
		node.OrderBy.Format(buf)
	}
	if node.Limit != nil {
		buf.WriteString(" ")
		node.Limit.Format(buf)
	}
}

// String returns the string representation
func (node *Select) String() string {
	return String(node)
}

// Insert represents an INSERT statement
type Insert struct {
	Action      string
	Ignore      bool
	Table       TableName
	Partitions  Partitions
	Columns     Columns
	Rows        InsertRows
	OnDup       OnDup
}

// iStatement implements Statement
func (node *Insert) iStatement() {}

// Format formats the node
func (node *Insert) Format(buf *TrackedBuffer) {
	buf.WriteString("insert")
	if node.Ignore {
		buf.WriteString(" ignore")
	}
	buf.WriteString(" into ")
	node.Table.Format(buf)
	if len(node.Columns) > 0 {
		buf.WriteString(" (")
		node.Columns.Format(buf)
		buf.WriteString(")")
	}
	if node.Rows != nil {
		buf.WriteString(" ")
		node.Rows.Format(buf)
	}
}

// String returns the string representation
func (node *Insert) String() string {
	return String(node)
}

// Update represents an UPDATE statement
type Update struct {
	Ignore     bool
	TableExprs TableExprs
	Exprs      UpdateExprs
	Where      *Where
	OrderBy    OrderBy
	Limit      *Limit
}

// iStatement implements Statement
func (node *Update) iStatement() {}

// Format formats the node
func (node *Update) Format(buf *TrackedBuffer) {
	buf.WriteString("update")
	if node.Ignore {
		buf.WriteString(" ignore")
	}
	buf.WriteString(" ")
	node.TableExprs.Format(buf)
	buf.WriteString(" set ")
	node.Exprs.Format(buf)
	if node.Where != nil {
		buf.WriteString(" ")
		node.Where.Format(buf)
	}
	if len(node.OrderBy) > 0 {
		buf.WriteString(" order by ")
		node.OrderBy.Format(buf)
	}
	if node.Limit != nil {
		buf.WriteString(" ")
		node.Limit.Format(buf)
	}
}

// String returns the string representation
func (node *Update) String() string {
	return String(node)
}

// Delete represents a DELETE statement
type Delete struct {
	Ignore     bool
	Targets    TableNames
	TableExprs TableExprs
	Where      *Where
	OrderBy    OrderBy
	Limit      *Limit
}

// iStatement implements Statement
func (node *Delete) iStatement() {}

// Format formats the node
func (node *Delete) Format(buf *TrackedBuffer) {
	buf.WriteString("delete")
	if node.Ignore {
		buf.WriteString(" ignore")
	}
	if len(node.Targets) > 0 {
		buf.WriteString(" ")
		node.Targets.Format(buf)
	}
	buf.WriteString(" from ")
	node.TableExprs.Format(buf)
	if node.Where != nil {
		buf.WriteString(" ")
		node.Where.Format(buf)
	}
	if len(node.OrderBy) > 0 {
		buf.WriteString(" order by ")
		node.OrderBy.Format(buf)
	}
	if node.Limit != nil {
		buf.WriteString(" ")
		node.Limit.Format(buf)
	}
}

// String returns the string representation
func (node *Delete) String() string {
	return String(node)
}

// Expression implementations

// ColName represents a column name
type ColName struct {
	Metadata  *Metadata
	Name      IdentifierCI
	Qualifier TableName
}

// iExpr implements Expr
func (node *ColName) iExpr() {}

// Format formats the node
func (node *ColName) Format(buf *TrackedBuffer) {
	if !node.Qualifier.IsEmpty() {
		node.Qualifier.Format(buf)
		buf.WriteByte('.')
	}
	node.Name.Format(buf)
}

// String returns the string representation
func (node *ColName) String() string {
	return String(node)
}

// Literal represents a literal value
type Literal struct {
	Type LiteralType
	Val  string
}

// LiteralType represents the type of a literal
type LiteralType int

// Literal types
const (
	StrVal LiteralType = iota
	IntVal
	FloatVal
	HexNum
	HexVal
	BitVal
	DateVal
	TimeVal
	TimestampVal
)

// iExpr implements Expr
func (node *Literal) iExpr() {}

// Format formats the node
func (node *Literal) Format(buf *TrackedBuffer) {
	switch node.Type {
	case StrVal:
		buf.WriteString("'")
		buf.WriteString(node.Val)
		buf.WriteString("'")
	default:
		buf.WriteString(node.Val)
	}
}

// String returns the string representation
func (node *Literal) String() string {
	return String(node)
}

// NullVal represents a NULL value
type NullVal struct{}

// iExpr implements Expr
func (node *NullVal) iExpr() {}

// Format formats the node
func (node *NullVal) Format(buf *TrackedBuffer) {
	buf.WriteString("null")
}

// String returns the string representation
func (node *NullVal) String() string {
	return String(node)
}

// Table expression implementations

// AliasedTableExpr represents a table expression with an optional alias
type AliasedTableExpr struct {
	Expr       SimpleTableExpr
	Partitions Partitions
	As         IdentifierCS
	Hints      *IndexHints
}

// iTableExpr implements TableExpr
func (node *AliasedTableExpr) iTableExpr() {}

// Format formats the node
func (node *AliasedTableExpr) Format(buf *TrackedBuffer) {
	node.Expr.Format(buf)
	if !node.As.IsEmpty() {
		buf.WriteString(" as ")
		node.As.Format(buf)
	}
}

// String returns the string representation
func (node *AliasedTableExpr) String() string {
	return String(node)
}

// SimpleTableExpr represents a simple table expression
type SimpleTableExpr interface {
	SQLNode
	iSimpleTableExpr()
}

// iSimpleTableExpr implements SimpleTableExpr for TableName
func (node TableName) iSimpleTableExpr() {}

// Subquery represents a subquery expression
type Subquery struct {
	Select *Select
}

// iSimpleTableExpr implements SimpleTableExpr
func (node *StarExpr) iSimpleTableExpr() {}

// iExpr implements Expr
func (node *StarExpr) iExpr() {}

// iSimpleTableExpr implements SimpleTableExpr
func (node *Subquery) iSimpleTableExpr() {}

// iExpr implements Expr
func (node *Subquery) iExpr() {}

// Format formats the node
func (node *Subquery) Format(buf *TrackedBuffer) {
	buf.WriteByte('(')
	node.Select.Format(buf)
	buf.WriteByte(')')
}

// String returns the string representation
func (node *Subquery) String() string {
	return String(node)
}

// JoinTableExpr represents a join table expression
type JoinTableExpr struct {
	LeftExpr  TableExpr
	Join      string
	RightExpr TableExpr
	Condition JoinCondition
}

// iTableExpr implements TableExpr
func (node *JoinTableExpr) iTableExpr() {}

// Format formats the node
func (node *JoinTableExpr) Format(buf *TrackedBuffer) {
	node.LeftExpr.Format(buf)
	buf.WriteByte(' ')
	buf.WriteString(node.Join)
	buf.WriteByte(' ')
	node.RightExpr.Format(buf)
	if node.Condition != nil {
		buf.WriteByte(' ')
		node.Condition.Format(buf)
	}
}

// String returns the string representation
func (node *JoinTableExpr) String() string {
	return String(node)
}

// JoinCondition represents a join condition
type JoinCondition interface {
	SQLNode
	iJoinCondition()
}

// OnCondition represents an ON condition in a join
type OnCondition struct {
	Expr Expr
}

// iJoinCondition implements JoinCondition
func (node *OnCondition) iJoinCondition() {}

// Format formats the node
func (node *OnCondition) Format(buf *TrackedBuffer) {
	buf.WriteString("on ")
	node.Expr.Format(buf)
}

// String returns the string representation
func (node *OnCondition) String() string {
	return String(node)
}

// UsingCondition represents a USING condition in a join
type UsingCondition struct {
	Cols Columns
}

// iJoinCondition implements JoinCondition
func (node *UsingCondition) iJoinCondition() {}

// Format formats the node
func (node *UsingCondition) Format(buf *TrackedBuffer) {
	buf.WriteString("using (")
	node.Cols.Format(buf)
	buf.WriteByte(')')
}

// String returns the string representation
func (node *UsingCondition) String() string {
	return String(node)
}

// FuncExpr represents a function call expression
type FuncExpr struct {
	Qualifier TableName
	Name      IdentifierCI
	Distinct  bool
	Exprs     SelectExprs
}

// iExpr implements Expr
func (node *FuncExpr) iExpr() {}

// Format formats the node
func (node *FuncExpr) Format(buf *TrackedBuffer) {
	if !node.Qualifier.IsEmpty() {
		node.Qualifier.Format(buf)
		buf.WriteByte('.')
	}
	node.Name.Format(buf)
	buf.WriteByte('(')
	if node.Distinct {
		buf.WriteString("distinct ")
	}
	node.Exprs.Format(buf)
	buf.WriteByte(')')
}

// String returns the string representation
func (node *FuncExpr) String() string {
	return String(node)
}

// Select expression implementations

// AliasedExpr represents a select expression with an optional alias
type AliasedExpr struct {
	Expr Expr
	As   IdentifierCI
}

// iSelectExpr implements SelectExpr
func (node *AliasedExpr) iSelectExpr() {}

// Format formats the node
func (node *AliasedExpr) Format(buf *TrackedBuffer) {
	node.Expr.Format(buf)
	if !node.As.IsEmpty() {
		buf.WriteString(" as ")
		node.As.Format(buf)
	}
}

// String returns the string representation
func (node *AliasedExpr) String() string {
	return String(node)
}

// StarExpr represents a '*' expression
type StarExpr struct {
	TableName TableName
}

// iSelectExpr implements SelectExpr
func (node *StarExpr) iSelectExpr() {}

// Format formats the node
func (node *StarExpr) Format(buf *TrackedBuffer) {
	if !node.TableName.IsEmpty() {
		node.TableName.Format(buf)
		buf.WriteByte('.')
	}
	buf.WriteByte('*')
}

// String returns the string representation
func (node *StarExpr) String() string {
	return String(node)
}

// Where represents a WHERE or HAVING clause
type Where struct {
	Type WhereType
	Expr Expr
}

// WhereType represents the type of WHERE clause
type WhereType string

// Where types
const (
	WhereStr  WhereType = "where"
	HavingStr WhereType = "having"
)

// Format formats the node
func (node *Where) Format(buf *TrackedBuffer) {
	buf.WriteString(string(node.Type))
	buf.WriteString(" ")
	node.Expr.Format(buf)
}

// String returns the string representation
func (node *Where) String() string {
	return String(node)
}

// Limit represents a LIMIT clause
type Limit struct {
	Offset, Rowcount Expr
}

// Format formats the node
func (node *Limit) Format(buf *TrackedBuffer) {
	buf.WriteString("limit")
	if node.Offset != nil {
		buf.WriteString(" ")
		node.Offset.Format(buf)
		buf.WriteString(",")
	}
	buf.WriteString(" ")
	node.Rowcount.Format(buf)
}

// String returns the string representation
func (node *Limit) String() string {
	return String(node)
}

// Collection types

// SelectExprs represents a list of select expressions
type SelectExprs []SelectExpr

// Format formats the node
func (node SelectExprs) Format(buf *TrackedBuffer) {
	for i, expr := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		expr.Format(buf)
	}
}

// String returns the string representation
func (node SelectExprs) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// TableExprs represents a list of table expressions
type TableExprs []TableExpr

// Format formats the node
func (node TableExprs) Format(buf *TrackedBuffer) {
	for i, expr := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		expr.Format(buf)
	}
}

// String returns the string representation
func (node TableExprs) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// TableNames represents a list of table names
type TableNames []TableName

// Format formats the node
func (node TableNames) Format(buf *TrackedBuffer) {
	for i, name := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		name.Format(buf)
	}
}

// String returns the string representation
func (node TableNames) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// Columns represents a list of column names
type Columns []IdentifierCI

// Format formats the node
func (node Columns) Format(buf *TrackedBuffer) {
	for i, col := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		col.Format(buf)
	}
}

// String returns the string representation
func (node Columns) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// GroupBy represents a GROUP BY clause
type GroupBy []Expr

// Format formats the node
func (node GroupBy) Format(buf *TrackedBuffer) {
	for i, expr := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		expr.Format(buf)
	}
}

// String returns the string representation
func (node GroupBy) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// OrderBy represents an ORDER BY clause
type OrderBy []*Order

// Format formats the node
func (node OrderBy) Format(buf *TrackedBuffer) {
	for i, order := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		order.Format(buf)
	}
}

// String returns the string representation
func (node OrderBy) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// Order represents an ORDER BY expression
type Order struct {
	Expr      Expr
	Direction OrderDirection
}

// OrderDirection represents the direction of ordering
type OrderDirection string

// Order directions
const (
	AscScr  OrderDirection = "asc"
	DescScr OrderDirection = "desc"
)

// Format formats the node
func (node *Order) Format(buf *TrackedBuffer) {
	node.Expr.Format(buf)
	if node.Direction != "" {
		buf.WriteString(" ")
		buf.WriteString(string(node.Direction))
	}
}

// String returns the string representation
func (node *Order) String() string {
	return String(node)
}

// UpdateExprs represents a list of update expressions
type UpdateExprs []*UpdateExpr

// Format formats the node
func (node UpdateExprs) Format(buf *TrackedBuffer) {
	for i, expr := range node {
		if i > 0 {
			buf.WriteString(", ")
		}
		expr.Format(buf)
	}
}

// String returns the string representation
func (node UpdateExprs) String() string {
	var buf strings.Builder
	trackBuf := NewTrackedBuffer(&buf)
	node.Format(trackBuf)
	return buf.String()
}

// UpdateExpr represents an update expression
type UpdateExpr struct {
	Name *ColName
	Expr Expr
}

// Format formats the node
func (node *UpdateExpr) Format(buf *TrackedBuffer) {
	node.Name.Format(buf)
	buf.WriteString(" = ")
	node.Expr.Format(buf)
}

// String returns the string representation
func (node *UpdateExpr) String() string {
	return String(node)
}

// Placeholder types for missing implementations

// Partitions represents table partitions
type Partitions []IdentifierCI

// Format formats the node
func (node Partitions) Format(buf *TrackedBuffer) {
	// Implementation placeholder
}

// InsertRows represents insert rows
type InsertRows interface {
	SQLNode
	iInsertRows()
}

// OnDup represents ON DUPLICATE KEY UPDATE
type OnDup UpdateExprs

// Format formats the node
func (node OnDup) Format(buf *TrackedBuffer) {
	// Implementation placeholder
}

// Lock represents table locking
type Lock string

// IndexHints represents index hints
type IndexHints struct {
	Type    string
	Indexes []IdentifierCI
}

// Format formats the node
func (node *IndexHints) Format(buf *TrackedBuffer) {
	// Implementation placeholder
}

// Metadata represents column metadata
type Metadata struct {
	// Implementation placeholder
}

// DDL Statements

// CreateTableStatement represents a CREATE TABLE statement
type CreateTableStatement struct {
	Table       *TableName
	Columns     []*ColumnDefinition
	Constraints []*TableConstraint
	Options     []*TableOption
	Partition   *PartitionClause
	Temporary   bool
	IfNotExists bool
}

// ColumnDefinition represents a column definition in CREATE TABLE
type ColumnDefinition struct {
	Name         *ColName
	Type         *ColumnType
	NotNull      bool
	AutoIncrement bool
	Default      Expr
	Comment      string
	PrimaryKey   bool
	Unique       bool
}

// ColumnType represents a column data type
type ColumnType struct {
	Type     string
	Length   int
	Scale    int
	Unsigned bool
	Zerofill bool
}

// TableConstraint represents table constraints
type TableConstraint struct {
	Type        string // PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK
	Name        string
	Columns     []*ColName
	References  *ForeignKeyReference
	CheckExpr   Expr
}

// ForeignKeyReference represents foreign key reference
type ForeignKeyReference struct {
	Table   *TableName
	Columns []*ColName
	OnDelete string
	OnUpdate string
}

// TableOption represents table options like ENGINE, CHARSET
type TableOption struct {
	Name  string
	Value string
}

// AlterTableStatement represents an ALTER TABLE statement
type AlterTableStatement struct {
	Table   *TableName
	Actions []*AlterAction
}

// AlterAction represents different ALTER TABLE actions
type AlterAction struct {
	Type   string // ADD, DROP, MODIFY, CHANGE, RENAME
	Column *ColumnDefinition
	OldName *ColName
	NewName *ColName
	Constraint *TableConstraint
	After   *ColName
	First   bool
}

// DropTableStatement represents a DROP TABLE statement
type DropTableStatement struct {
	Tables   []*TableName
	IfExists bool
	Temporary bool
}

// CreateIndexStatement represents a CREATE INDEX statement
type CreateIndexStatement struct {
	Index    string
	Table    *TableName
	Columns  []*ColName
	Unique   bool
	IfNotExists bool
}

// DropIndexStatement represents a DROP INDEX statement
type DropIndexStatement struct {
	Index    string
	Table    *TableName
	IfExists bool
}

// TruncateStatement represents a TRUNCATE TABLE statement
type TruncateStatement struct {
	Table *TableName
}

// CreateViewStatement represents a CREATE VIEW statement
type CreateViewStatement struct {
	View        *TableName
	Columns     Columns
	Select      *Select
	Replace     bool
	Temporary   bool
	IfNotExists bool
	Algorithm   string
	Definer     string
	SQLSecurity string
	CheckOption string
}

// DropViewStatement represents a DROP VIEW statement
type DropViewStatement struct {
	Views     []*TableName
	IfExists  bool
	Restrict  bool
	Cascade   bool
}

// AlterViewStatement represents an ALTER VIEW statement
type AlterViewStatement struct {
	View        *TableName
	Columns     Columns
	Select      *Select
	Algorithm   string
	Definer     string
	SQLSecurity string
	CheckOption string
}

// Implement Node interface for DDL statements

func (node *CreateTableStatement) iNode() {}
func (node *CreateViewStatement) iNode() {}
func (node *DropViewStatement) iNode() {}
func (node *AlterViewStatement) iNode() {}
func (node *CreateTableStatement) String() string {
	return String(node)
}

func (node *ColumnDefinition) iNode() {}
func (node *ColumnDefinition) String() string {
	return "COLUMN DEFINITION"
}

func (node *ColumnType) iNode() {}
func (node *ColumnType) String() string {
	return node.Type
}

func (node *TableConstraint) iNode() {}
func (node *TableConstraint) String() string {
	return node.Type
}

func (node *ForeignKeyReference) iNode() {}
func (node *ForeignKeyReference) String() string {
	return "FOREIGN KEY REFERENCE"
}

func (node *TableOption) iNode() {}
func (node *TableOption) String() string {
	return node.Name
}

func (node *AlterTableStatement) iNode() {}
func (node *AlterTableStatement) String() string {
	return String(node)
}

func (node *AlterAction) iNode() {}
func (node *AlterAction) String() string {
	return node.Type
}

func (node *DropTableStatement) iNode() {}
func (node *DropTableStatement) String() string {
	return String(node)
}

func (node *CreateIndexStatement) iNode() {}
func (node *CreateIndexStatement) String() string {
	return String(node)
}

func (node *DropIndexStatement) iNode() {}
func (node *DropIndexStatement) String() string {
	return String(node)
}

func (node *TruncateStatement) iNode() {}
func (node *TruncateStatement) String() string {
	return String(node)
}

func (node *CreateViewStatement) String() string {
	return String(node)
}

func (node *DropViewStatement) String() string {
	return String(node)
}

func (node *AlterViewStatement) String() string {
	return String(node)
}

func (node *BeginStatement) String() string {
	return String(node)
}

func (node *CommitStatement) String() string {
	return String(node)
}

func (node *RollbackStatement) String() string {
	return String(node)
}

// Implement Statement interface for DDL statements

func (node *CreateTableStatement) iStatement() {}
func (node *CreateTableStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("CREATE")
	if node.Temporary {
		buf.WriteString(" TEMPORARY")
	}
	buf.WriteString(" TABLE")
	if node.IfNotExists {
		buf.WriteString(" IF NOT EXISTS")
	}
	if node.Table != nil {
		buf.WriteString(" ")
		node.Table.Format(buf)
	}
}

func (node *AlterTableStatement) iStatement() {}
func (node *AlterTableStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("ALTER TABLE ")
	node.Table.Format(buf)
}

func (node *DropTableStatement) iStatement() {}
func (node *DropTableStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("DROP")
	if node.Temporary {
		buf.WriteString(" TEMPORARY")
	}
	buf.WriteString(" TABLE")
	if node.IfExists {
		buf.WriteString(" IF EXISTS")
	}
	buf.WriteString(" ")
	for i, table := range node.Tables {
		if i > 0 {
			buf.WriteString(", ")
		}
		table.Format(buf)
	}
}

func (node *CreateIndexStatement) iStatement() {}
func (node *CreateIndexStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("CREATE")
	if node.Unique {
		buf.WriteString(" UNIQUE")
	}
	buf.WriteString(" INDEX")
	if node.IfNotExists {
		buf.WriteString(" IF NOT EXISTS")
	}
	buf.WriteString(" ")
	buf.WriteString(node.Index)
	buf.WriteString(" ON ")
	node.Table.Format(buf)
}

func (node *DropIndexStatement) iStatement() {}
func (node *DropIndexStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("DROP INDEX")
	if node.IfExists {
		buf.WriteString(" IF EXISTS")
	}
	buf.WriteString(" ")
	buf.WriteString(node.Index)
	if node.Table != nil {
		buf.WriteString(" ON ")
		node.Table.Format(buf)
	}
}

func (node *TruncateStatement) iStatement() {}
func (node *TruncateStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("TRUNCATE TABLE ")
	node.Table.Format(buf)
}

func (node *CreateViewStatement) iStatement() {}
func (node *CreateViewStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("CREATE")
	if node.Replace {
		buf.WriteString(" OR REPLACE")
	}
	if node.Algorithm != "" {
		buf.WriteString(" ALGORITHM = ")
		buf.WriteString(node.Algorithm)
	}
	if node.Definer != "" {
		buf.WriteString(" DEFINER = ")
		buf.WriteString(node.Definer)
	}
	if node.SQLSecurity != "" {
		buf.WriteString(" SQL SECURITY ")
		buf.WriteString(node.SQLSecurity)
	}
	buf.WriteString(" VIEW ")
	node.View.Format(buf)
	if len(node.Columns) > 0 {
		buf.WriteString(" (")
		node.Columns.Format(buf)
		buf.WriteString(")")
	}
	buf.WriteString(" AS ")
	node.Select.Format(buf)
	if node.CheckOption != "" {
		buf.WriteString(" WITH ")
		buf.WriteString(node.CheckOption)
		buf.WriteString(" CHECK OPTION")
	}
}

func (node *DropViewStatement) iStatement() {}
func (node *DropViewStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("DROP VIEW")
	if node.IfExists {
		buf.WriteString(" IF EXISTS")
	}
	buf.WriteString(" ")
	for i, view := range node.Views {
		if i > 0 {
			buf.WriteString(", ")
		}
		view.Format(buf)
	}
	if node.Restrict {
		buf.WriteString(" RESTRICT")
	} else if node.Cascade {
		buf.WriteString(" CASCADE")
	}
}

func (node *AlterViewStatement) iStatement() {}

// Transaction control statements

// BeginStatement represents a BEGIN/START TRANSACTION statement
type BeginStatement struct {
	Work bool // true for "BEGIN WORK"
}

// CommitStatement represents a COMMIT statement
type CommitStatement struct {
	Work bool // true for "COMMIT WORK"
}

// RollbackStatement represents a ROLLBACK statement
type RollbackStatement struct {
	Work bool // true for "ROLLBACK WORK"
	To   *ColName // for ROLLBACK TO SAVEPOINT
}

// iStatement implements Statement
func (node *BeginStatement) iStatement() {}
func (node *CommitStatement) iStatement() {}
func (node *RollbackStatement) iStatement() {}
func (node *AlterViewStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("ALTER")
	if node.Algorithm != "" {
		buf.WriteString(" ALGORITHM = ")
		buf.WriteString(node.Algorithm)
	}
	if node.Definer != "" {
		buf.WriteString(" DEFINER = ")
		buf.WriteString(node.Definer)
	}
	if node.SQLSecurity != "" {
		buf.WriteString(" SQL SECURITY ")
		buf.WriteString(node.SQLSecurity)
	}
	buf.WriteString(" VIEW ")
	node.View.Format(buf)
	if len(node.Columns) > 0 {
		buf.WriteString(" (")
		node.Columns.Format(buf)
		buf.WriteString(")")
	}
	buf.WriteString(" AS ")
	node.Select.Format(buf)
	if node.CheckOption != "" {
		buf.WriteString(" WITH ")
		buf.WriteString(node.CheckOption)
		buf.WriteString(" CHECK OPTION")
	}
}

// Format methods for transaction control statements

func (node *BeginStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("BEGIN")
	if node.Work {
		buf.WriteString(" WORK")
	}
}

func (node *CommitStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("COMMIT")
	if node.Work {
		buf.WriteString(" WORK")
	}
}

func (node *RollbackStatement) Format(buf *TrackedBuffer) {
	buf.WriteString("ROLLBACK")
	if node.Work {
		buf.WriteString(" WORK")
	}
	if node.To != nil {
		buf.WriteString(" TO SAVEPOINT ")
		node.To.Format(buf)
	}
}

// Partition-related AST nodes

// PartitionDefinition represents a partition definition
type PartitionDefinition struct {
	Name        IdentifierCI
	Type        string // RANGE, LIST, HASH, KEY
	Expression  Expr   // Partitioning expression
	Values      []Expr // VALUES LESS THAN or VALUES IN
	Subpartitions []*SubpartitionDefinition
	Options     []*PartitionOption
}

// SubpartitionDefinition represents a subpartition definition
type SubpartitionDefinition struct {
	Name    IdentifierCI
	Options []*PartitionOption
}

// PartitionOption represents partition options like DATA DIRECTORY, INDEX DIRECTORY
type PartitionOption struct {
	Name  string
	Value string
}

// PartitionClause represents the PARTITION BY clause
type PartitionClause struct {
	Type        string // RANGE, LIST, HASH, KEY, LINEAR HASH, LINEAR KEY
	Expression  Expr   // Partitioning expression
	Columns     Columns // For RANGE COLUMNS, LIST COLUMNS
	Partitions  int    // Number of partitions
	SubpartitionBy *SubpartitionClause
	PartitionDefs []*PartitionDefinition
}

// SubpartitionClause represents the SUBPARTITION BY clause
type SubpartitionClause struct {
	Type        string // HASH, KEY, LINEAR HASH, LINEAR KEY
	Expression  Expr   // Subpartitioning expression
	Columns     Columns // For KEY subpartitioning
	Subpartitions int  // Number of subpartitions
}

// Format methods for partition nodes

func (node *PartitionDefinition) Format(buf *TrackedBuffer) {
	buf.WriteString("PARTITION ")
	node.Name.Format(buf)
	if node.Type == "RANGE" || node.Type == "LIST" {
		if node.Type == "RANGE" {
			buf.WriteString(" VALUES LESS THAN (")
		} else {
			buf.WriteString(" VALUES IN (")
		}
		for i, val := range node.Values {
			if i > 0 {
				buf.WriteString(", ")
			}
			val.Format(buf)
		}
		buf.WriteString(")")
	}
	if len(node.Options) > 0 {
		for _, opt := range node.Options {
			buf.WriteString(" ")
			opt.Format(buf)
		}
	}
}

func (node *SubpartitionDefinition) Format(buf *TrackedBuffer) {
	buf.WriteString("SUBPARTITION ")
	node.Name.Format(buf)
	for _, opt := range node.Options {
		buf.WriteString(" ")
		opt.Format(buf)
	}
}

func (node *PartitionOption) Format(buf *TrackedBuffer) {
	buf.WriteString(node.Name)
	if node.Value != "" {
		buf.WriteString(" = ")
		buf.WriteString(node.Value)
	}
}

func (node *PartitionClause) Format(buf *TrackedBuffer) {
	buf.WriteString("PARTITION BY ")
	buf.WriteString(node.Type)
	if node.Expression != nil {
		buf.WriteString(" (")
		node.Expression.Format(buf)
		buf.WriteString(")")
	} else if len(node.Columns) > 0 {
		buf.WriteString(" COLUMNS (")
		node.Columns.Format(buf)
		buf.WriteString(")")
	}
	if node.Partitions > 0 {
		buf.WriteString(" PARTITIONS ")
		buf.WriteString(fmt.Sprintf("%d", node.Partitions))
	}
	if node.SubpartitionBy != nil {
		buf.WriteString(" ")
		node.SubpartitionBy.Format(buf)
	}
	if len(node.PartitionDefs) > 0 {
		buf.WriteString(" (")
		for i, def := range node.PartitionDefs {
			if i > 0 {
				buf.WriteString(", ")
			}
			def.Format(buf)
		}
		buf.WriteString(")")
	}
}

func (node *SubpartitionClause) Format(buf *TrackedBuffer) {
	buf.WriteString("SUBPARTITION BY ")
	buf.WriteString(node.Type)
	if node.Expression != nil {
		buf.WriteString(" (")
		node.Expression.Format(buf)
		buf.WriteString(")")
	} else if len(node.Columns) > 0 {
		buf.WriteString(" COLUMNS (")
		node.Columns.Format(buf)
		buf.WriteString(")")
	}
	if node.Subpartitions > 0 {
		buf.WriteString(" SUBPARTITIONS ")
		buf.WriteString(fmt.Sprintf("%d", node.Subpartitions))
	}
}

// String methods for partition nodes

func (node *PartitionDefinition) String() string {
	return String(node)
}

func (node *SubpartitionDefinition) String() string {
	return String(node)
}

func (node *PartitionOption) String() string {
	return String(node)
}

func (node *PartitionClause) String() string {
	return String(node)
}

func (node *SubpartitionClause) String() string {
	return String(node)
}

// BinaryExpr represents a binary expression
type BinaryExpr struct {
	Operator string
	Left     Expr
	Right    Expr
}

// iExpr implements Expr
func (node *BinaryExpr) iExpr() {}

// Format formats the node
func (node *BinaryExpr) Format(buf *TrackedBuffer) {
	buf.WriteString("(")
	node.Left.Format(buf)
	buf.WriteString(" ")
	buf.WriteString(node.Operator)
	buf.WriteString(" ")
	node.Right.Format(buf)
	buf.WriteString(")")
}

// String returns the string representation
func (node *BinaryExpr) String() string {
	return String(node)
}

// CaseExpr represents a CASE expression
type CaseExpr struct {
	Expr  Expr
	Whens []*When
	Else  Expr
}

// iExpr implements Expr
func (node *CaseExpr) iExpr() {}

// Format formats the node
func (node *CaseExpr) Format(buf *TrackedBuffer) {
	buf.WriteString("case")
	if node.Expr != nil {
		buf.WriteString(" ")
		node.Expr.Format(buf)
	}
	for _, when := range node.Whens {
		when.Format(buf)
	}
	if node.Else != nil {
		buf.WriteString(" else ")
		node.Else.Format(buf)
	}
	buf.WriteString(" end")
}

// String returns the string representation
func (node *CaseExpr) String() string {
	return String(node)
}

// When represents a WHEN clause in a CASE expression
type When struct {
	Cond Expr
	Val  Expr
}

// Format formats the node
func (node *When) Format(buf *TrackedBuffer) {
	buf.WriteString(" when ")
	node.Cond.Format(buf)
	buf.WriteString(" then ")
	node.Val.Format(buf)
}

// String returns the string representation
func (node *When) String() string {
	return String(node)
}

// Operator constants for binary expressions
const (
	OrStr           = "or"
	AndStr          = "and"
	EqualStr        = "="
	NotEqualStr     = "!="
	LessThanStr     = "<"
	LessEqualStr    = "<="
	GreaterThanStr  = ">"
	GreaterEqualStr = ">="
	LikeStr         = "like"
	NotLikeStr      = "not like"
	InStr           = "in"
	NotInStr        = "not in"
	IsStr           = "is"
	IsNotStr        = "is not"
	PlusStr         = "+"
	MinusStr        = "-"
	MultStr         = "*"
	DivStr          = "/"
	ModStr          = "%"
)

// ParenExpr represents a parenthesized expression
type ParenExpr struct {
	Expr Expr
}

// iExpr implements Expr
func (node *ParenExpr) iExpr() {}

// Format formats the node
func (node *ParenExpr) Format(buf *TrackedBuffer) {
	buf.WriteString("(")
	node.Expr.Format(buf)
	buf.WriteString(")")
}

// String returns the string representation
func (node *ParenExpr) String() string {
	return String(node)
}

// BoolVal represents a boolean value
type BoolVal struct {
	Val bool
}

// iExpr implements Expr
func (node *BoolVal) iExpr() {}

// Format formats the node
func (node *BoolVal) Format(buf *TrackedBuffer) {
	if node.Val {
		buf.WriteString("true")
	} else {
		buf.WriteString("false")
	}
}

// String returns the string representation
func (node *BoolVal) String() string {
	return String(node)
}

// ExistsExpr represents an EXISTS expression
type ExistsExpr struct {
	Subquery *Subquery
}

// iExpr implements Expr
func (node *ExistsExpr) iExpr() {}

// Format formats the node
func (node *ExistsExpr) Format(buf *TrackedBuffer) {
	buf.WriteString("exists ")
	node.Subquery.Format(buf)
}

// String returns the string representation
func (node *ExistsExpr) String() string {
	return String(node)
}

// ComparisonExpr represents a comparison expression
type ComparisonExpr struct {
	Left     Expr
	Operator string
	Right    Expr
}

// iExpr implements Expr
func (node *ComparisonExpr) iExpr() {}

// Format formats the node
func (node *ComparisonExpr) Format(buf *TrackedBuffer) {
	buf.WriteString("(")
	node.Left.Format(buf)
	buf.WriteString(" ")
	buf.WriteString(node.Operator)
	buf.WriteString(" ")
	node.Right.Format(buf)
	buf.WriteString(")")
}

// String returns the string representation
func (node *ComparisonExpr) String() string {
	return String(node)
}

// TupleExpr represents a tuple expression (for IN clauses)
type TupleExpr struct {
	Exprs []Expr
}

// iExpr implements Expr
func (node *TupleExpr) iExpr() {}

// Format formats the node
func (node *TupleExpr) Format(buf *TrackedBuffer) {
	buf.WriteString("(")
	for i, expr := range node.Exprs {
		if i > 0 {
			buf.WriteString(", ")
		}
		expr.Format(buf)
	}
	buf.WriteString(")")
}

// String returns the string representation
func (node *TupleExpr) String() string {
	return String(node)
}